home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / emacs.lha / emacs-19.16 / lisp / forms.el < prev    next >
Text File  |  1993-07-04  |  40KB  |  1,295 lines

  1. ;;; forms.el -- Forms mode: edit a file as a form to fill in.
  2. ;;; Copyright (C) 1991, 1993 Free Software Foundation, Inc.
  3.  
  4. ;; Author: Johan Vromans <jv@mh.nl>
  5. ;; Version: 1.2.14
  6. ;; Keywords: non-text
  7.  
  8. ;; This file is part of GNU Emacs.
  9.  
  10. ;; GNU Emacs is free software; you can redistribute it and/or modify
  11. ;; it under the terms of the GNU General Public License as published by
  12. ;; the Free Software Foundation; either version 2, or (at your option)
  13. ;; any later version.
  14.  
  15. ;; GNU Emacs is distributed in the hope that it will be useful,
  16. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. ;; GNU General Public License for more details.
  19.  
  20. ;; You should have received a copy of the GNU General Public License
  21. ;; along with GNU Emacs; see the file COPYING.  If not, write to
  22. ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  
  24. ;;; Commentary:
  25.  
  26. ;;; Visit a file using a form.
  27. ;;;
  28. ;;; === Naming conventions
  29. ;;;
  30. ;;; The names of all variables and functions start with 'form-'.
  31. ;;; Names which start with 'form--' are intended for internal use, and
  32. ;;; should *NOT* be used from the outside.
  33. ;;;
  34. ;;; All variables are buffer-local, to enable multiple forms visits 
  35. ;;; simultaneously.
  36. ;;; Variable 'forms--mode-setup' is local to *ALL* buffers, for it 
  37. ;;; controls if forms-mode has been enabled in a buffer.
  38. ;;;
  39. ;;; === How it works ===
  40. ;;;
  41. ;;; Forms mode means visiting a data file which is supposed to consist
  42. ;;; of records each containing a number of fields. The records are
  43. ;;; separated by a newline, the fields are separated by a user-defined
  44. ;;; field separater (default: TAB).
  45. ;;; When shown, a record is transferred to an emacs buffer and
  46. ;;; presented using a user-defined form.  One record is shown at a
  47. ;;; time.
  48. ;;;
  49. ;;; Forms mode is a composite mode.  It involves two files, and two
  50. ;;; buffers.
  51. ;;; The first file, called the control file, defines the name of the
  52. ;;; data file and the forms format.  This file buffer will be used to
  53. ;;; present the forms.
  54. ;;; The second file holds the actual data.  The buffer of this file
  55. ;;; will be buried, for it is never accessed directly.
  56. ;;;
  57. ;;; Forms mode is invoked using "forms-find-file control-file".
  58. ;;; Alternativily forms-find-file-other-window can be used.
  59. ;;;
  60. ;;; You may also visit the control file, and switch to forms mode by hand
  61. ;;; with M-x forms-mode .
  62. ;;;
  63. ;;; Automatic mode switching is supported, so you may use "find-file"
  64. ;;; if you specify "-*- forms -*-" in the first line of the control file.
  65. ;;; 
  66. ;;; The control file is visited, evaluated using
  67. ;;; eval-current-buffer, and should set at least the following
  68. ;;; variables:
  69. ;;;
  70. ;;;    forms-file            [string] the name of the data file.
  71. ;;;
  72. ;;;    forms-number-of-fields        [integer]
  73. ;;;            The number of fields in each record.
  74. ;;;
  75. ;;;    forms-format-list           [list]   formatting instructions.
  76. ;;;
  77. ;;; The forms-format-list should be a list, each element containing
  78. ;;;
  79. ;;;  - a string, e.g. "hello" (which is inserted \"as is\"),
  80. ;;;
  81. ;;;  - an integer, denoting a field number.  The contents of the field
  82. ;;;    are inserted at this point.
  83. ;;;    The first field has number one.
  84. ;;;
  85. ;;;  - a function call, e.g. (insert "text").  This function call is 
  86. ;;;    dynamically evaluated and should return a string.  It should *NOT*
  87. ;;;    have side-effects on the forms being constructed.
  88. ;;;    The current fields are available to the function in the variable
  89. ;;;    forms-fields, they should *NOT* be modified.
  90. ;;;
  91. ;;;  - a lisp symbol, that must evaluate to one of the above.
  92. ;;;
  93. ;;; Optional variables which may be set in the control file:
  94. ;;;
  95. ;;;    forms-field-sep                [string, default TAB]
  96. ;;;            The field separator used to separate the
  97. ;;;            fields in the data file.  It may be a string.
  98. ;;;
  99. ;;;    forms-read-only                [bool, default nil]
  100. ;;;            't' means that the data file is visited read-only.
  101. ;;;            If no write access to the data file is
  102. ;;;            possible, read-only mode is enforced. 
  103. ;;;
  104. ;;;    forms-multi-line            [string, default "^K"]
  105. ;;;            If non-null the records of the data file may
  106. ;;;            contain fields which span multiple lines in
  107. ;;;            the form.
  108. ;;;            This variable denoted the separator character
  109. ;;;            to be used for this purpose.  Upon display, all
  110. ;;;            occurrencies of this character are translated
  111. ;;;            to newlines.  Upon storage they are translated
  112. ;;;            back to the separator.
  113. ;;;
  114. ;;;    forms-forms-scroll            [bool, default t]
  115. ;;;            If non-nil: redefine scroll-up/down to perform
  116. ;;;            forms-next/prev-field if in forms mode.
  117. ;;;
  118. ;;;    forms-forms-jump            [bool, default t]
  119. ;;;            If non-nil: redefine beginning/end-of-buffer
  120. ;;;            to performs forms-first/last-field if in
  121. ;;;            forms mode.
  122. ;;;
  123. ;;;    forms-new-record-filter            [symbol, no default]
  124. ;;;            If defined: this should be the name of a 
  125. ;;;            function that is called when a new
  126. ;;;            record is created.  It can be used to fill in
  127. ;;;            the new record with default fields, for example.
  128. ;;;            Instead of the name of the function, it may
  129. ;;;            be the function itself.
  130. ;;;
  131. ;;;    forms-modified-record-filter        [symbol, no default]
  132. ;;;            If defined: this should be the name of a 
  133. ;;;            function that is called when a record has
  134. ;;;            been modified.  It is called after the fields
  135. ;;;            are parsed.  It can be used to register
  136. ;;;            modification dates, for example.
  137. ;;;            Instead of the name of the function, it may
  138. ;;;            be the function itself.
  139. ;;;
  140. ;;; After evaluating the control file, its buffer is cleared and used
  141. ;;; for further processing.
  142. ;;; The data file (as designated by "forms-file") is visited in a buffer
  143. ;;; (forms--file-buffer) which will not normally be shown.
  144. ;;; Great malfunctioning may be expected if this file/buffer is modified
  145. ;;; outside of this package while it's being visited!
  146. ;;;
  147. ;;; A record from the data file is transferred from the data file,
  148. ;;; split into fields (into forms--the-record-list), and displayed using
  149. ;;; the specs in forms-format-list.
  150. ;;; A format routine 'forms--format' is built upon startup to format 
  151. ;;; the records.
  152. ;;;
  153. ;;; When a form is changed the record is updated as soon as this form
  154. ;;; is left.  The contents of the form are parsed using forms-format-list,
  155. ;;; and the fields which are deduced from the form are modified.  So,
  156. ;;; fields not shown on the forms retain their origional values.
  157. ;;; The newly formed record and replaces the contents of the
  158. ;;; old record in forms--file-buffer.
  159. ;;; A parse routine 'forms--parser' is built upon startup to parse
  160. ;;; the records.
  161. ;;;
  162. ;;; Two exit functions exist: forms-exit (which saves) and forms-exit-no-save
  163. ;;; (which doesn't).  However, if forms-exit-no-save is executed and the file
  164. ;;; buffer has been modified, emacs will ask questions.
  165. ;;;
  166. ;;; Other functions are:
  167. ;;;
  168. ;;;    paging (forward, backward) by record
  169. ;;;    jumping (first, last, random number)
  170. ;;;    searching
  171. ;;;    creating and deleting records
  172. ;;;    reverting the form (NOT the file buffer)
  173. ;;;    switching edit <-> view mode v.v.
  174. ;;;    jumping from field to field
  175. ;;;
  176. ;;; As an documented side-effect: jumping to the last record in the
  177. ;;; file (using forms-last-record) will adjust forms--total-records if
  178. ;;; needed.
  179. ;;;
  180. ;;; Commands and keymaps:
  181. ;;;
  182. ;;; A local keymap 'forms-mode-map' is used in the forms buffer.
  183. ;;; As conventional, this map can be accessed with C-c prefix.
  184. ;;; In read-only mode, the C-c prefix must be omitted.
  185. ;;;
  186. ;;; Default bindings:
  187. ;;;
  188. ;;;    \C-c    forms-mode-map
  189. ;;;    TAB    forms-next-field
  190. ;;;    SPC     forms-next-record
  191. ;;;    <    forms-first-record
  192. ;;;    >    forms-last-record
  193. ;;;    ?    describe-mode
  194. ;;;    d    forms-delete-record
  195. ;;;    e    forms-edit-mode
  196. ;;;    i    forms-insert-record
  197. ;;;    j    forms-jump-record
  198. ;;;    n    forms-next-record
  199. ;;;    p    forms-prev-record
  200. ;;;    q    forms-exit
  201. ;;;    s    forms-search
  202. ;;;    v    forms-view-mode
  203. ;;;    x    forms-exit-no-save
  204. ;;;    DEL    forms-prev-record
  205. ;;;
  206. ;;; The bindings of standard functions scroll-up, scroll-down,
  207. ;;; beginning-of-buffer and end-of-buffer are locally replaced with
  208. ;;; forms mode functions next/prev record and first/last
  209. ;;; record.  Buffer-local variables forms-forms-scroll and
  210. ;;; forms-forms-jump (default: t) may be set to nil to inhibit
  211. ;;; rebinding.
  212. ;;;
  213. ;;; A local-write-file hook is defined to save the actual data file
  214. ;;; instead of the buffer data, a revert-file-hook is defined to
  215. ;;; revert a forms to original.
  216. ;;;
  217. ;;; For convenience, TAB is always bound to forms-next-field, so you
  218. ;;; don't need the C-c prefix for this command.
  219.  
  220. ;;; Code:
  221.  
  222. ;;; Global variables and constants
  223.  
  224. (provide 'forms)            ;;; official
  225. (provide 'forms-mode)            ;;; for compatibility
  226.  
  227. (defconst forms-version "1.2.14"
  228.   "Version of forms-mode implementation.")
  229.  
  230. (defvar forms-mode-hooks nil
  231.   "Hook functions to be run upon entering Forms mode.")
  232.  
  233. ;;; Mandatory variables - must be set by evaluating the control file
  234.  
  235. (defvar forms-file nil
  236.   "Name of the file holding the data.")
  237.  
  238. (defvar forms-format-list nil
  239.   "List of formatting specifications.")
  240.  
  241. (defvar forms-number-of-fields nil
  242.   "Number of fields per record.")
  243.  
  244. ;;; Optional variables with default values
  245.  
  246. (defvar forms-field-sep "\t"
  247.   "Field separator character (default TAB).")
  248.  
  249. (defvar forms-read-only nil
  250.   "Read-only mode (defaults to the write access on the data file).")
  251.  
  252. (defvar forms-multi-line "\C-k"
  253.   "Character to separate multi-line fields (default C-k).")
  254.  
  255. (defvar forms-forms-scroll t
  256.   "*Non-nil means replace scroll-up/down commands in Forms mode.
  257. The replacement commands performs forms-next/prev-record.")
  258.  
  259. (defvar forms-forms-jump t
  260.   "*Non-nil means redefine beginning/end-of-buffer in Forms mode.
  261. The replacement commands performs forms-first/last-record.")
  262.  
  263. ;;; Internal variables.
  264.  
  265. (defvar forms--file-buffer nil
  266.   "Buffer which holds the file data")
  267.  
  268. (defvar forms--total-records 0
  269.   "Total number of records in the data file.")
  270.  
  271. (defvar forms--current-record 0
  272.   "Number of the record currently on the screen.")
  273.  
  274. (defvar forms-mode-map nil        ; yes - this one is global
  275.    "Keymap for form buffer.")
  276.  
  277. (defvar forms--markers nil
  278.   "Field markers in the screen.")
  279.  
  280. (defvar forms--number-of-markers 0
  281.   "Number of fields on screen.")
  282.  
  283. (defvar forms--the-record-list nil 
  284.    "List of strings of the current record, as parsed from the file.")
  285.  
  286. (defvar forms--search-regexp nil
  287.   "Last regexp used by forms-search.")
  288.  
  289. (defvar forms--format nil
  290.   "Formatting routine.")
  291.  
  292. (defvar forms--parser nil
  293.   "Forms parser routine.")
  294.  
  295. (defvar forms--mode-setup nil
  296.   "Internal - keeps track of forms-mode being set-up.")
  297. (make-variable-buffer-local 'forms--mode-setup)
  298.  
  299. (defvar forms--new-record-filter nil
  300.   "Internal - set if a new record filter has been defined.")
  301.  
  302. (defvar forms--modified-record-filter nil
  303.   "Internal - set if a modified record filter has been defined.")
  304.  
  305. (defvar forms--dynamic-text nil
  306.   "Internal - holds dynamic text to insert between fields.")
  307.  
  308. (defvar forms-fields nil
  309.   "List with fields of the current forms.  First field has number 1.")
  310.  
  311. (defvar forms-new-record-filter nil
  312.   "The name of a function that is called when a new record is created.")
  313.  
  314. (defvar forms-modified-record-filter nil
  315.   "The name of a function that is called when a record has been modified.")
  316.  
  317. ;;; forms-mode
  318. ;;;
  319. ;;; This is not a simple major mode, as usual.  Therefore, forms-mode
  320. ;;; takes an optional argument 'primary' which is used for the initial
  321. ;;; set-up.  Normal use would leave 'primary' to nil.
  322. ;;;
  323. ;;; A global buffer-local variable 'forms--mode-setup' has the same effect
  324. ;;; but makes it possible to auto-invoke forms-mode using find-file.
  325. ;;;
  326. ;;; Note: although it seems logical to have (make-local-variable) executed
  327. ;;; where the variable is first needed, I deliberately placed all calls
  328. ;;; in the forms-mode function.
  329.  
  330. ;;;###autoload 
  331. (defun forms-mode (&optional primary)
  332.   "Major mode to visit files in a field-structured manner using a form.
  333.  
  334. Commands (prefix with C-c if not in read-only mode):
  335. \\{forms-mode-map}"
  336.  
  337.   (interactive)                ; no - 'primary' is not prefix arg
  338.  
  339.   ;; Primary set-up: evaluate buffer and check if the mandatory
  340.   ;; variables have been set.
  341.   (if (or primary (not forms--mode-setup))
  342.       (progn
  343.     (kill-all-local-variables)
  344.  
  345.     ;; make mandatory variables
  346.     (make-local-variable 'forms-file)
  347.     (make-local-variable 'forms-number-of-fields)
  348.     (make-local-variable 'forms-format-list)
  349.  
  350.     ;; make optional variables
  351.     (make-local-variable 'forms-field-sep)
  352.         (make-local-variable 'forms-read-only)
  353.         (make-local-variable 'forms-multi-line)
  354.     (make-local-variable 'forms-forms-scroll)
  355.     (make-local-variable 'forms-forms-jump)
  356.     (fmakunbound 'forms-new-record-filter)
  357.  
  358.     ;; eval the buffer, should set variables
  359.     (eval-current-buffer)
  360.  
  361.     ;; check if the mandatory variables make sense.
  362.     (or forms-file
  363.         (error "'forms-file' has not been set"))
  364.     (or forms-number-of-fields
  365.         (error "'forms-number-of-fields' has not been set"))
  366.     (or (> forms-number-of-fields 0)
  367.         (error "'forms-number-of-fields' must be > 0")
  368.     (or (stringp forms-field-sep))
  369.         (error "'forms-field-sep' is not a string"))
  370.     (if forms-multi-line
  371.         (if (and (stringp forms-multi-line)
  372.              (eq (length forms-multi-line) 1))
  373.         (if (string= forms-multi-line forms-field-sep)
  374.             (error "'forms-multi-line' is equal to 'forms-field-sep'"))
  375.           (error "'forms-multi-line' must be nil or a one-character string")))
  376.         
  377.     ;; validate and process forms-format-list
  378.     (make-local-variable 'forms--number-of-markers)
  379.     (make-local-variable 'forms--markers)
  380.     (forms--process-format-list)
  381.  
  382.     ;; build the formatter and parser
  383.     (make-local-variable 'forms--format)
  384.     (forms--make-format)
  385.     (make-local-variable 'forms--parser)
  386.     (forms--make-parser)
  387.  
  388.     ;; check if record filters are defined
  389.     (make-local-variable 'forms--new-record-filter)
  390.     (setq forms--new-record-filter 
  391.           (cond
  392.            ((fboundp 'forms-new-record-filter)
  393.         (symbol-function 'forms-new-record-filter))
  394.            ((and (boundp 'forms-new-record-filter)
  395.              (fboundp forms-new-record-filter))
  396.         forms-new-record-filter)))
  397.     (fmakunbound 'forms-new-record-filter)
  398.     (make-local-variable 'forms--modified-record-filter)
  399.     (setq forms--modified-record-filter 
  400.           (cond
  401.            ((fboundp 'forms-modified-record-filter)
  402.         (symbol-function 'forms-modified-record-filter))
  403.            ((and (boundp 'forms-modified-record-filter)
  404.              (fboundp forms-modified-record-filter))
  405.         forms-modified-record-filter)))
  406.     (fmakunbound 'forms-modified-record-filter)
  407.  
  408.     ;; dynamic text support
  409.     (make-local-variable 'forms--dynamic-text)
  410.     (make-local-variable 'forms-fields)
  411.  
  412.     ;; prepare this buffer for further processing
  413.     (setq buffer-read-only nil)
  414.  
  415.     ;; prevent accidental overwrite of the control file and autosave
  416.     (setq buffer-file-name nil)
  417.     (auto-save-mode nil)
  418.  
  419.     ;; and clean it
  420.     (erase-buffer)))
  421.  
  422.   ;; Make more local variables.
  423.   (make-local-variable 'forms--file-buffer)
  424.   (make-local-variable 'forms--total-records)
  425.   (make-local-variable 'forms--current-record)
  426.   (make-local-variable 'forms--the-record-list)
  427.   (make-local-variable 'forms--search-rexexp)
  428.  
  429.   ;; A bug in the current Emacs release prevents a keymap
  430.   ;; which is buffer-local from being used by 'describe-mode'.
  431.   ;; Hence we'll leave it global.
  432.   ;;(make-local-variable 'forms-mode-map)
  433.   (if forms-mode-map            ; already defined
  434.       nil
  435.     (setq forms-mode-map (make-keymap))
  436.     (forms--mode-commands forms-mode-map))
  437.  
  438.   ;; find the data file
  439.   (setq forms--file-buffer (find-file-noselect forms-file))
  440.  
  441.   ;; count the number of records, and set see if it may be modified
  442.   (let (ro)
  443.     (setq forms--total-records
  444.       (save-excursion
  445.         (set-buffer forms--file-buffer)
  446.         (bury-buffer (current-buffer))
  447.         (setq ro buffer-read-only)
  448.         (count-lines (point-min) (point-max))))
  449.     (if ro
  450.     (setq forms-read-only t)))
  451.  
  452.   ;; set the major mode indicator
  453.   (setq major-mode 'forms-mode)
  454.   (setq mode-name "Forms")
  455.   (make-local-variable 'minor-mode-alist) ; needed?
  456.   (forms--set-minor-mode)
  457.   (forms--set-keymaps)
  458.   (make-local-variable 'local-write-file-hooks)
  459.   (forms--change-commands)
  460.  
  461.   (set-buffer-modified-p nil)
  462.  
  463.   ;; We have our own revert function - use it
  464.   (make-local-variable 'revert-buffer-function)
  465.   (setq revert-buffer-function 'forms-revert-buffer)
  466.  
  467.   ;; setup the first (or current) record to show
  468.   (if (< forms--current-record 1)
  469.       (setq forms--current-record 1))
  470.   (forms-jump-record forms--current-record)
  471.  
  472.   ;; user customising
  473.   (run-hooks 'forms-mode-hooks)
  474.  
  475.   ;; be helpful
  476.   (forms--help)
  477.  
  478.   ;; initialization done
  479.   (setq forms--mode-setup t))
  480.  
  481. ;;; forms-process-format-list
  482. ;;;
  483. ;;; Validates forms-format-list.
  484. ;;; Sets forms--number-of-markers and forms--markers.
  485.  
  486. (defun forms--process-format-list ()
  487.   "Validate forms-format-list and set some global variables."
  488.  
  489.   (forms--debug "forms-forms-list before 1st pass:\n"
  490.         'forms-format-list)
  491.  
  492.   ;; it must be non-nil
  493.   (or forms-format-list
  494.       (error "'forms-format-list' has not been set"))
  495.   ;; it must be a list ...
  496.   (or (listp forms-format-list)
  497.       (error "'forms-format-list' is not a list"))
  498.  
  499.   (setq forms--number-of-markers 0)
  500.  
  501.   (let ((the-list forms-format-list)    ; the list of format elements
  502.     (this-item 0)            ; element in list
  503.     (field-num 0))            ; highest field number 
  504.  
  505.     (setq forms-format-list nil)    ; gonna rebuild
  506.  
  507.     (while the-list
  508.  
  509.       (let ((el (car-safe the-list))
  510.         (rem (cdr-safe the-list)))
  511.  
  512.     ;; if it is a symbol, eval it first
  513.     (if (and (symbolp el)
  514.          (boundp el))
  515.         (setq el (eval el)))
  516.  
  517.     (cond
  518.  
  519.      ;; try string ...
  520.      ((stringp el))            ; string is OK
  521.       
  522.      ;; try numeric ...
  523.      ((numberp el) 
  524.  
  525.       (if (or (<= el 0)
  526.           (> el forms-number-of-fields))
  527.           (error
  528.            "Forms error: field number %d out of range 1..%d"
  529.            el forms-number-of-fields))
  530.  
  531.       (setq forms--number-of-markers (1+ forms--number-of-markers))
  532.       (if (> el field-num)
  533.           (setq field-num el)))
  534.  
  535.      ;; try function
  536.      ((listp el)
  537.       (or (fboundp (car-safe el))
  538.           (error 
  539.            "Forms error: not a function: %s"
  540.            (prin1-to-string (car-safe el)))))
  541.  
  542.      ;; else
  543.      (t
  544.       (error "Invalid element in 'forms-format-list': %s"
  545.          (prin1-to-string el))))
  546.  
  547.     ;; advance to next element of the list
  548.     (setq the-list rem)
  549.     (setq forms-format-list
  550.           (append forms-format-list (list el) nil)))))
  551.  
  552.   (forms--debug "forms-forms-list after 1st pass:\n"
  553.         'forms-format-list)
  554.  
  555.   ;; concat adjacent strings
  556.   (setq forms-format-list (forms--concat-adjacent forms-format-list))
  557.  
  558.   (forms--debug "forms-forms-list after 2nd pass:\n"
  559.         'forms-format-list
  560.         'forms--number-of-markers)
  561.  
  562.   (setq forms--markers (make-vector forms--number-of-markers nil)))
  563.  
  564. ;;; Build the format routine from forms-format-list.
  565. ;;;
  566. ;;; The format routine (forms--format) will look like
  567. ;;; 
  568. ;;; (lambda (arg)
  569. ;;;   (setq forms--dynamic-text nil)
  570. ;;;   ;;  "text: "
  571. ;;;   (insert "text: ")
  572. ;;;   ;;  6
  573. ;;;   (aset forms--markers 0 (point-marker))
  574. ;;;   (insert (elt arg 5))
  575. ;;;   ;;  "\nmore text: "
  576. ;;;   (insert "\nmore text: ")
  577. ;;;   ;;  (tocol 40)
  578. ;;;   (let ((the-dyntext (tocol 40)))
  579. ;;;     (insert the-dyntext)
  580. ;;;     (setq forms--dynamic-text (append forms--dynamic-text
  581. ;;;                      (list the-dyntext))))
  582. ;;;   ;;  9
  583. ;;;   (aset forms--markers 1 (point-marker))
  584. ;;;   (insert (elt arg 8))
  585. ;;;
  586. ;;;   ... )
  587. ;;; 
  588.  
  589. (defun forms--make-format ()
  590.   "Generate format function for forms."
  591.   (setq forms--format (forms--format-maker forms-format-list))
  592.   (forms--debug 'forms--format))
  593.  
  594. (defun forms--format-maker (the-format-list)
  595.   "Returns the parser function for forms."
  596.   (let ((the-marker 0))
  597.     (` (lambda (arg)
  598.      (setq forms--dynamic-text nil)
  599.      (,@ (apply 'append
  600.             (mapcar 'forms--make-format-elt the-format-list)))))))
  601.  
  602. (defun forms--make-format-elt (el)
  603.   (cond 
  604.    ((stringp el)
  605.     (` ((insert (, el)))))
  606.    ((numberp el)
  607.     (prog1
  608.     (` ((aset forms--markers (, the-marker) (point-marker))
  609.         (insert (elt arg (, (1- el))))))
  610.       (setq the-marker (1+ the-marker))))
  611.    ((listp el)
  612.     (prog1
  613.     (` ((let ((the-dyntext (, el)))
  614.           (insert the-dyntext)
  615.           (setq forms--dynamic-text (append forms--dynamic-text
  616.                         (list the-dyntext)))))
  617.        )))))
  618.  
  619. (defun forms--concat-adjacent (the-list)
  620.   "Concatenate adjacent strings in the-list and return the resulting list."
  621.   (if (consp the-list)
  622.       (let ((the-rest (forms--concat-adjacent (cdr the-list))))
  623.     (if (and (stringp (car the-list)) (stringp (car the-rest)))
  624.         (cons (concat (car the-list) (car the-rest))
  625.           (cdr the-rest))
  626.         (cons (car the-list) the-rest)))
  627.       the-list))
  628.  
  629. ;;; forms--make-parser.
  630. ;;;
  631. ;;; Generate parse routine from forms-format-list.
  632. ;;;
  633. ;;; The parse routine (forms--parser) will look like (give or take
  634. ;;; a few " " .
  635. ;;; 
  636. ;;; (lambda nil
  637. ;;;   (let (here)
  638. ;;;     (goto-char (point-min))
  639. ;;; 
  640. ;;;    ;;  "text: "
  641. ;;;     (if (not (looking-at "text: "))
  642. ;;;         (error "Parse error: cannot find \"text: \""))
  643. ;;;     (forward-char 6)    ; past "text: "
  644. ;;; 
  645. ;;;     ;;  6
  646. ;;;    ;;  "\nmore text: "
  647. ;;;     (setq here (point))
  648. ;;;     (if (not (search-forward "\nmore text: " nil t nil))
  649. ;;;         (error "Parse error: cannot find \"\\nmore text: \""))
  650. ;;;     (aset the-recordv 5 (buffer-substring here (- (point) 12)))
  651. ;;;
  652. ;;;    ;;  (tocol 40)
  653. ;;;    (let ((the-dyntext (car-safe forms--dynamic-text)))
  654. ;;;      (if (not (looking-at (regexp-quote the-dyntext)))
  655. ;;;          (error "Parse error: not looking at \"%s\"" the-dyntext))
  656. ;;;      (forward-char (length the-dyntext))
  657. ;;;      (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))
  658. ;;;     ... 
  659. ;;;     ;; final flush (due to terminator sentinel, see below)
  660. ;;;    (aset the-recordv 7 (buffer-substring (point) (point-max)))
  661. ;;; 
  662.  
  663. (defun forms--make-parser ()
  664.   "Generate parser function for forms."
  665.   (setq forms--parser (forms--parser-maker forms-format-list))
  666.   (forms--debug 'forms--parser))
  667.  
  668. (defun forms--parser-maker (the-format-list)
  669.   "Returns the parser function for forms."
  670.   (let ((the-field nil)
  671.     (seen-text nil)
  672.     the--format-list)
  673.     ;; add a terminator sentinel
  674.     (setq the--format-list (append the-format-list (list nil)))
  675.     (` (lambda nil
  676.      (let (here)
  677.        (goto-char (point-min))
  678.      (,@ (apply 'append
  679.             (mapcar 'forms--make-parser-elt the--format-list))))))))
  680.  
  681. (defun forms--make-parser-elt (el)
  682.   (cond
  683.    ((stringp el)
  684.     (prog1
  685.     (if the-field
  686.         (` ((setq here (point))
  687.         (if (not (search-forward (, el) nil t nil))
  688.             (error "Parse error: cannot find \"%s\"" (, el)))
  689.         (aset the-recordv (, (1- the-field))
  690.               (buffer-substring here
  691.                     (- (point) (, (length el)))))))
  692.       (` ((if (not (looking-at (, (regexp-quote el))))
  693.           (error "Parse error: not looking at \"%s\"" (, el)))
  694.           (forward-char (, (length el))))))
  695.       (setq seen-text t)
  696.       (setq the-field nil)))
  697.    ((numberp el)
  698.     (if the-field
  699.     (error "Cannot parse adjacent fields %d and %d"
  700.            the-field el)
  701.       (setq the-field el)
  702.       nil))
  703.    ((null el)
  704.     (if the-field
  705.     (` ((aset the-recordv (, (1- the-field))
  706.           (buffer-substring (point) (point-max)))))))
  707.    ((listp el)
  708.     (prog1
  709.     (if the-field
  710.         (` ((let ((here (point))
  711.               (the-dyntext (car-safe forms--dynamic-text)))
  712.           (if (not (search-forward the-dyntext nil t nil))
  713.               (error "Parse error: cannot find \"%s\"" the-dyntext))
  714.           (aset the-recordv (, (1- the-field))
  715.             (buffer-substring here
  716.                       (- (point) (length the-dyntext))))
  717.           (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))))
  718.       (` ((let ((the-dyntext (car-safe forms--dynamic-text)))
  719.         (if (not (looking-at (regexp-quote the-dyntext)))
  720.             (error "Parse error: not looking at \"%s\"" the-dyntext))
  721.         (forward-char (length the-dyntext))
  722.         (setq forms--dynamic-text (cdr-safe forms--dynamic-text))))))
  723.       (setq seen-text t)
  724.       (setq the-field nil)))
  725.    ))
  726.  
  727. (defun forms--set-minor-mode ()
  728.   (setq minor-mode-alist
  729.     (if forms-read-only
  730.         " View"
  731.       nil)))
  732.  
  733. (defun forms--set-keymaps ()
  734.   "Set the keymaps used in this mode."
  735.  
  736.   (if forms-read-only
  737.       (use-local-map forms-mode-map)
  738.     (use-local-map (make-sparse-keymap))
  739.     (define-key (current-local-map) "\C-c" forms-mode-map)
  740.     (define-key (current-local-map) "\t"   'forms-next-field)))
  741.  
  742. (defun forms--mode-commands (map)
  743.   "Fill map with all Forms mode commands."
  744.   (define-key map "\t" 'forms-next-field)
  745.   (define-key map " " 'forms-next-record)
  746.   (define-key map "d" 'forms-delete-record)
  747.   (define-key map "e" 'forms-edit-mode)
  748.   (define-key map "i" 'forms-insert-record)
  749.   (define-key map "j" 'forms-jump-record)
  750.   (define-key map "n" 'forms-next-record)
  751.   (define-key map "p" 'forms-prev-record)
  752.   (define-key map "q" 'forms-exit)
  753.   (define-key map "s" 'forms-search)
  754.   (define-key map "v" 'forms-view-mode)
  755.   (define-key map "x" 'forms-exit-no-save)
  756.   (define-key map "<" 'forms-first-record)
  757.   (define-key map ">" 'forms-last-record)
  758.   (define-key map "?" 'describe-mode)
  759.   (define-key map "\177" 'forms-prev-record)
  760.  ;  (define-key map "\C-c" map)
  761.   (define-key map "\e" 'ESC-prefix)
  762.   (define-key map "\C-x" ctl-x-map)
  763.   (define-key map "\C-u" 'universal-argument)
  764.   (define-key map "\C-h" help-map)
  765.   )
  766.  
  767. ;;; Changed functions
  768.  
  769. (defun forms--change-commands ()
  770.   "Localize some commands for Forms mode."
  771.   ;;
  772.   ;; scroll-down -> forms-prev-record
  773.   ;; scroll-up -> forms-next-record
  774.   (if forms-forms-scroll
  775.       (progn
  776.     (substitute-key-definition 'scroll-up 'forms-next-record
  777.                    (current-local-map)
  778.                    (current-global-map))
  779.     (substitute-key-definition 'scroll-down 'forms-prev-record
  780.                    (current-local-map)
  781.                    (current-global-map))))
  782.   ;;
  783.   ;; beginning-of-buffer -> forms-first-record
  784.   ;; end-of-buffer -> forms-end-record
  785.   (if forms-forms-jump
  786.       (progn
  787.     (substitute-key-definition 'beginning-of-buffer 'forms-first-record
  788.                    (current-local-map)
  789.                    (current-global-map))
  790.     (substitute-key-definition 'end-of-buffer 'forms-last-record
  791.                    (current-local-map)
  792.                    (current-global-map))))
  793.   ;;
  794.   ;; save-buffer -> forms--save-buffer
  795.   (add-hook 'local-write-file-hooks
  796.         (function
  797.          (lambda (nil)
  798.            (forms--checkmod)
  799.            (save-excursion
  800.          (set-buffer forms--file-buffer)
  801.          (save-buffer))
  802.            t))))
  803.  
  804. (defun forms--help ()
  805.   "Initial help for Forms mode."
  806.   ;; We should use
  807.   ;;(message (substitute-command-keys (concat
  808.   ;;"\\[forms-next-record]:next"
  809.   ;;"   \\[forms-prev-record]:prev"
  810.   ;;"   \\[forms-first-record]:first"
  811.   ;;"   \\[forms-last-record]:last"
  812.   ;;"   \\[describe-mode]:help"
  813.   ;;"   \\[forms-exit]:exit")))
  814.   ;; but it's too slow ....
  815.   (if forms-read-only
  816.       (message "SPC:next   DEL:prev   <:first   >:last   ?:help   q:exit")
  817.     (message "C-c n:next   C-c p:prev   C-c <:first   C-c >:last   C-c ?:help   C-c q:exit")))
  818.  
  819. (defun forms--trans (subj arg rep)
  820.   "Translate in SUBJ all chars ARG into char REP.  ARG and REP should
  821.  be single-char strings."
  822.   (let ((i 0)
  823.     (x (length subj))
  824.     (re (regexp-quote arg))
  825.     (k (string-to-char rep)))
  826.     (while (setq i (string-match re subj i))
  827.       (aset subj i k)
  828.       (setq i (1+ i)))))
  829.  
  830. (defun forms--exit (query &optional save)
  831.   (let ((buf (buffer-name forms--file-buffer)))
  832.     (forms--checkmod)
  833.     (if (and save
  834.          (buffer-modified-p forms--file-buffer))
  835.     (save-excursion
  836.       (set-buffer forms--file-buffer)
  837.       (save-buffer)))
  838.     (save-excursion
  839.       (set-buffer forms--file-buffer)
  840.       (delete-auto-save-file-if-necessary)
  841.       (kill-buffer (current-buffer)))
  842.     (if (get-buffer buf)    ; not killed???
  843.       (if save
  844.       (progn
  845.         (beep)
  846.         (message "Problem saving buffers?")))
  847.       (delete-auto-save-file-if-necessary)
  848.       (kill-buffer (current-buffer)))))
  849.  
  850. (defun forms--get-record ()
  851.   "Fetch the current record from the file buffer."
  852.   ;;
  853.   ;; This function is executed in the context of the forms--file-buffer.
  854.   ;;
  855.   (or (bolp)
  856.       (beginning-of-line nil))
  857.   (let ((here (point)))
  858.     (prog2
  859.      (end-of-line)
  860.      (buffer-substring here (point))
  861.      (goto-char here))))
  862.  
  863. (defun forms--show-record (the-record)
  864.   "Format THE-RECORD and display it in the current buffer."
  865.  
  866.   ;; split the-record
  867.   (let (the-result
  868.     (start-pos 0)
  869.     found-pos
  870.     (field-sep-length (length forms-field-sep)))
  871.     (if forms-multi-line
  872.     (forms--trans the-record forms-multi-line "\n"))
  873.     ;; add an extra separator (makes splitting easy)
  874.     (setq the-record (concat the-record forms-field-sep))
  875.     (while (setq found-pos (string-match forms-field-sep the-record start-pos))
  876.       (let ((ent (substring the-record start-pos found-pos)))
  877.     (setq the-result
  878.           (append the-result (list ent)))
  879.     (setq start-pos (+ field-sep-length found-pos))))
  880.     (setq forms--the-record-list the-result))
  881.  
  882.   (setq buffer-read-only nil)
  883.   (erase-buffer)
  884.  
  885.   ;; verify the number of fields, extend forms--the-record-list if needed
  886.   (if (= (length forms--the-record-list) forms-number-of-fields)
  887.       nil
  888.     (beep)
  889.     (message "Record has %d fields instead of %d."
  890.          (length forms--the-record-list) forms-number-of-fields)
  891.     (if (< (length forms--the-record-list) forms-number-of-fields)
  892.     (setq forms--the-record-list 
  893.           (append forms--the-record-list
  894.               (make-list 
  895.                (- forms-number-of-fields 
  896.               (length forms--the-record-list))
  897.                "")))))
  898.  
  899.   ;; call the formatter function
  900.   (setq forms-fields (append (list nil) forms--the-record-list nil))
  901.   (funcall forms--format forms--the-record-list)
  902.  
  903.   ;; prepare
  904.   (goto-char (point-min))
  905.   (set-buffer-modified-p nil)
  906.   (setq buffer-read-only forms-read-only)
  907.   (setq mode-line-process
  908.     (concat " " forms--current-record "/" forms--total-records)))
  909.  
  910. (defun forms--parse-form ()
  911.   "Parse contents of form into list of strings."
  912.   ;; The contents of the form are parsed, and a new list of strings
  913.   ;; is constructed.
  914.   ;; A vector with the strings from the original record is 
  915.   ;; constructed, which is updated with the new contents.  Therefore
  916.   ;; fields which were not in the form are not modified.
  917.   ;; Finally, the vector is transformed into a list for further processing.
  918.  
  919.   (let (the-recordv)
  920.  
  921.     ;; build the vector
  922.     (setq the-recordv (vconcat forms--the-record-list))
  923.  
  924.     ;; parse the form and update the vector
  925.     (let ((forms--dynamic-text forms--dynamic-text))
  926.       (funcall forms--parser))
  927.  
  928.     (if forms--modified-record-filter
  929.     ;; As a service to the user, we add a zeroth element so she
  930.     ;; can use the same indices as in the forms definition.
  931.     (let ((the-fields (vconcat [nil] the-recordv)))
  932.       (setq the-fields (funcall forms--modified-record-filter the-fields))
  933.       (cdr (append the-fields nil)))
  934.  
  935.       ;; transform to a list and return
  936.       (append the-recordv nil))))
  937.  
  938. (defun forms--update ()
  939.   "Update current record with contents of form.
  940. As a side effect: sets forms--the-record-list ."
  941.  
  942.   (if forms-read-only
  943.       (progn
  944.     (message "Read-only buffer!")
  945.     (beep))
  946.  
  947.     (let (the-record)
  948.       ;; build new record
  949.       (setq forms--the-record-list (forms--parse-form))
  950.       (setq the-record
  951.         (mapconcat 'identity forms--the-record-list forms-field-sep))
  952.  
  953.       ;; handle multi-line fields, if allowed
  954.       (if forms-multi-line
  955.       (forms--trans the-record "\n" forms-multi-line))
  956.  
  957.       ;; a final sanity check before updating
  958.       (if (string-match "\n" the-record)
  959.       (progn
  960.         (message "Multi-line fields in this record - update refused!")
  961.         (beep))
  962.  
  963.     (save-excursion
  964.       (set-buffer forms--file-buffer)
  965.       ;; Insert something before kill-line is called.  See kill-line
  966.       ;; doc.  Bugfix provided by Ignatios Souvatzis.
  967.       (insert "*")
  968.       (beginning-of-line)
  969.       (kill-line nil)
  970.       (insert the-record)
  971.       (beginning-of-line))))))
  972.  
  973. (defun forms--checkmod ()
  974.   "Check if this form has been modified, and call forms--update if so."
  975.   (if (buffer-modified-p nil)
  976.       (let ((here (point)))
  977.     (forms--update)
  978.     (set-buffer-modified-p nil)
  979.     (goto-char here))))
  980.  
  981. ;;; Start and exit
  982.  
  983. ;;;###autoload
  984. (defun forms-find-file (fn)
  985.   "Visit a file in Forms mode."
  986.   (interactive "fForms file: ")
  987.   (find-file-read-only fn)
  988.   (or forms--mode-setup (forms-mode t)))
  989.  
  990. ;;;###autoload
  991. (defun forms-find-file-other-window (fn)
  992.   "Visit a file in Forms mode in other window."
  993.   (interactive "fFbrowse file in other window: ")
  994.   (find-file-other-window fn)
  995.   (eval-current-buffer)
  996.   (or forms--mode-setup (forms-mode t)))
  997.  
  998. (defun forms-exit (query)
  999.   "Normal exit from Forms mode.  Modified buffers are saved."
  1000.   (interactive "P")
  1001.   (forms--exit query t))
  1002.  
  1003. (defun forms-exit-no-save (query)
  1004.   "Exit from Forms mode without saving buffers."
  1005.   (interactive "P")
  1006.   (forms--exit query nil))
  1007.  
  1008. ;;; Navigating commands
  1009.  
  1010. (defun forms-next-record (arg)
  1011.   "Advance to the ARGth following record."
  1012.   (interactive "P")
  1013.   (forms-jump-record (+ forms--current-record (prefix-numeric-value arg)) t))
  1014.  
  1015. (defun forms-prev-record (arg)
  1016.   "Advance to the ARGth previous record."
  1017.   (interactive "P")
  1018.   (forms-jump-record (- forms--current-record (prefix-numeric-value arg)) t))
  1019.  
  1020. (defun forms-jump-record (arg &optional relative)
  1021.   "Jump to a random record."
  1022.   (interactive "NRecord number: ")
  1023.  
  1024.   ;; verify that the record number is within range
  1025.   (if (or (> arg forms--total-records)
  1026.       (<= arg 0))
  1027.     (progn
  1028.       (beep)
  1029.       ;; don't give the message if just paging
  1030.       (if (not relative)
  1031.       (message "Record number %d out of range 1..%d"
  1032.            arg forms--total-records))
  1033.       )
  1034.  
  1035.     ;; flush
  1036.     (forms--checkmod)
  1037.  
  1038.     ;; calculate displacement
  1039.     (let ((disp (- arg forms--current-record))
  1040.       (cur forms--current-record))
  1041.  
  1042.       ;; forms--show-record needs it now
  1043.       (setq forms--current-record arg)
  1044.  
  1045.       ;; get the record and show it
  1046.       (forms--show-record
  1047.        (save-excursion
  1048.      (set-buffer forms--file-buffer)
  1049.      (beginning-of-line)
  1050.  
  1051.      ;; move, and adjust the amount if needed (shouldn't happen)
  1052.      (if relative
  1053.          (if (zerop disp)
  1054.          nil
  1055.            (setq cur (+ cur disp (- (forward-line disp)))))
  1056.        (setq cur (+ cur disp (- (goto-line arg)))))
  1057.  
  1058.      (forms--get-record)))
  1059.  
  1060.       ;; this shouldn't happen
  1061.       (if (/= forms--current-record cur)
  1062.       (progn
  1063.         (setq forms--current-record cur)
  1064.         (beep)
  1065.         (message "Stuck at record %d." cur))))))
  1066.  
  1067. (defun forms-first-record ()
  1068.   "Jump to first record."
  1069.   (interactive)
  1070.   (forms-jump-record 1))
  1071.  
  1072. (defun forms-last-record ()
  1073.   "Jump to last record.
  1074. As a side effect: re-calculates the number of records in the data file."
  1075.   (interactive)
  1076.   (let
  1077.       ((numrec 
  1078.     (save-excursion
  1079.       (set-buffer forms--file-buffer)
  1080.       (count-lines (point-min) (point-max)))))
  1081.     (if (= numrec forms--total-records)
  1082.     nil
  1083.       (beep)
  1084.       (setq forms--total-records numrec)
  1085.       (message "Number of records reset to %d." forms--total-records)))
  1086.   (forms-jump-record forms--total-records))
  1087.  
  1088. ;;; Other commands
  1089.  
  1090. (defun forms-view-mode ()
  1091.   "Visit buffer read-only."
  1092.   (interactive)
  1093.   (if forms-read-only
  1094.       nil
  1095.     (forms--checkmod)            ; sync
  1096.     (setq forms-read-only t)
  1097.     (forms-mode)))
  1098.  
  1099. (defun forms-edit-mode ()
  1100.   "Make form suitable for editing, if possible."
  1101.   (interactive)
  1102.   (let ((ro forms-read-only))
  1103.     (if (save-excursion
  1104.       (set-buffer forms--file-buffer)
  1105.       buffer-read-only)
  1106.     (progn
  1107.       (setq forms-read-only t)
  1108.       (message "No write access to \"%s\"" forms-file)
  1109.       (beep))
  1110.       (setq forms-read-only nil))
  1111.     (if (equal ro forms-read-only)
  1112.     nil
  1113.       (forms-mode))))
  1114.  
  1115. ;; Sample:
  1116. ;; (defun my-new-record-filter (the-fields)
  1117. ;;   ;; numbers are relative to 1
  1118. ;;   (aset the-fields 4 (current-time-string))
  1119. ;;   (aset the-fields 6 (user-login-name))
  1120. ;;   the-list)
  1121. ;; (setq forms-new-record-filter 'my-new-record-filter)
  1122.  
  1123. (defun forms-insert-record (arg)
  1124.   "Create a new record before the current one.
  1125. With ARG: store the record after the current one.
  1126. If a function forms-new-record-filter is defined, or 
  1127. forms-new-record-filter contains the name of a function, 
  1128. it is called to fill (some of) the fields with default values."
  1129.  ; The above doc is not true, but for documentary purposes only
  1130.  
  1131.   (interactive "P")
  1132.  
  1133.   (let ((ln (if arg (1+ forms--current-record) forms--current-record))
  1134.         the-list the-record)
  1135.  
  1136.     (forms--checkmod)
  1137.     (if forms--new-record-filter
  1138.     ;; As a service to the user, we add a zeroth element so she
  1139.     ;; can use the same indices as in the forms definition.
  1140.     (let ((the-fields (make-vector (1+ forms-number-of-fields) "")))
  1141.       (setq the-fields (funcall forms--new-record-filter the-fields))
  1142.       (setq the-list (cdr (append the-fields nil))))
  1143.       (setq the-list (make-list forms-number-of-fields "")))
  1144.  
  1145.     (setq the-record
  1146.       (mapconcat
  1147.       'identity
  1148.       the-list
  1149.       forms-field-sep))
  1150.  
  1151.     (save-excursion
  1152.       (set-buffer forms--file-buffer)
  1153.       (goto-line ln)
  1154.       (open-line 1)
  1155.       (insert the-record)
  1156.       (beginning-of-line))
  1157.     
  1158.     (setq forms--current-record ln))
  1159.  
  1160.   (setq forms--total-records (1+ forms--total-records))
  1161.   (forms-jump-record forms--current-record))
  1162.  
  1163. (defun forms-delete-record (arg)
  1164.   "Deletes a record.  With a prefix argument: don't ask."
  1165.   (interactive "P")
  1166.   (forms--checkmod)
  1167.   (if (or arg
  1168.       (y-or-n-p "Really delete this record? "))
  1169.       (let ((ln forms--current-record))
  1170.     (save-excursion
  1171.       (set-buffer forms--file-buffer)
  1172.       (goto-line ln)
  1173.       (kill-line 1))
  1174.     (setq forms--total-records (1- forms--total-records))
  1175.     (if (> forms--current-record forms--total-records)
  1176.         (setq forms--current-record forms--total-records))
  1177.     (forms-jump-record forms--current-record)))
  1178.   (message ""))
  1179.  
  1180. (defun forms-search (regexp)
  1181.   "Search REGEXP in file buffer."
  1182.   (interactive 
  1183.    (list (read-string (concat "Search for" 
  1184.                   (if forms--search-regexp
  1185.                    (concat " ("
  1186.                        forms--search-regexp
  1187.                        ")"))
  1188.                   ": "))))
  1189.   (if (equal "" regexp)
  1190.       (setq regexp forms--search-regexp))
  1191.   (forms--checkmod)
  1192.  
  1193.   (let (the-line the-record here
  1194.          (fld-sep forms-field-sep))
  1195.     (if (save-excursion
  1196.       (set-buffer forms--file-buffer)
  1197.       (setq here (point))
  1198.       (end-of-line)
  1199.       (if (null (re-search-forward regexp nil t))
  1200.           (progn
  1201.         (goto-char here)
  1202.         (message (concat "\"" regexp "\" not found."))
  1203.         nil)
  1204.         (setq the-record (forms--get-record))
  1205.         (setq the-line (1+ (count-lines (point-min) (point))))))
  1206.     (progn
  1207.       (setq forms--current-record the-line)
  1208.       (forms--show-record the-record)
  1209.       (re-search-forward regexp nil t))))
  1210.   (setq forms--search-regexp regexp))
  1211.  
  1212. (defun forms-revert-buffer (&optional arg noconfirm)
  1213.   "Reverts current form to un-modified."
  1214.   (interactive "P")
  1215.   (if (or noconfirm
  1216.       (yes-or-no-p "Revert form to unmodified? "))
  1217.       (progn
  1218.     (set-buffer-modified-p nil)
  1219.     (forms-jump-record forms--current-record))))
  1220.  
  1221. (defun forms-next-field (arg)
  1222.   "Jump to ARG-th next field."
  1223.   (interactive "p")
  1224.  
  1225.   (let ((i 0)
  1226.     (here (point))
  1227.     there
  1228.     (cnt 0))
  1229.  
  1230.     (if (zerop arg)
  1231.     (setq cnt 1)
  1232.       (setq cnt (+ cnt arg)))
  1233.  
  1234.     (if (catch 'done
  1235.       (while (< i forms--number-of-markers)
  1236.         (if (or (null (setq there (aref forms--markers i)))
  1237.             (<= there here))
  1238.         nil
  1239.           (if (<= (setq cnt (1- cnt)) 0)
  1240.           (progn
  1241.             (goto-char there)
  1242.             (throw 'done t))))
  1243.         (setq i (1+ i))))
  1244.     nil
  1245.       (goto-char (aref forms--markers 0)))))
  1246.  
  1247. ;;;
  1248. ;;; Special service
  1249. ;;;
  1250. (defun forms-enumerate (the-fields)
  1251.   "Take a quoted list of symbols, and set their values to sequential numbers.
  1252. The first symbol gets number 1, the second 2 and so on.
  1253. It returns the higest number.
  1254.  
  1255. Usage: (setq forms-number-of-fields
  1256.              (forms-enumerate
  1257.               '(field1 field2 field2 ...)))"
  1258.  
  1259.   (let ((the-index 0))
  1260.     (while the-fields
  1261.       (setq the-index (1+ the-index))
  1262.       (let ((el (car-safe the-fields)))
  1263.     (setq the-fields (cdr-safe the-fields))
  1264.     (set el the-index)))
  1265.     the-index))
  1266.  
  1267. ;;; Debugging
  1268.  
  1269. (defvar forms--debug nil
  1270.   "*Enables forms-mode debugging if not nil.")
  1271.  
  1272. (defun forms--debug (&rest args)
  1273.   "Internal debugging routine."
  1274.   (if forms--debug
  1275.       (let ((ret nil))
  1276.     (while args
  1277.       (let ((el (car-safe args)))
  1278.         (setq args (cdr-safe args))
  1279.         (if (stringp el)
  1280.         (setq ret (concat ret el))
  1281.           (setq ret (concat ret (prin1-to-string el) " = "))
  1282.           (if (boundp el)
  1283.           (let ((vel (eval el)))
  1284.             (setq ret (concat ret (prin1-to-string vel) "\n")))
  1285.         (setq ret (concat ret "<unbound>" "\n")))
  1286.           (if (fboundp el)
  1287.           (setq ret (concat ret (prin1-to-string (symbol-function el)) 
  1288.                     "\n"))))))
  1289.     (save-excursion
  1290.       (set-buffer (get-buffer-create "*forms-mode debug*"))
  1291.       (goto-char (point-max))
  1292.       (insert ret)))))
  1293.  
  1294. ;;; forms.el ends here.
  1295.